package com.lan.playscript.model;

import org.antlr.v4.runtime.ParserRuleContext;

import java.util.List;

public class Clazz extends Scope implements Type {
    //父类
    private Clazz parentClazz = null; //= rootClass;

    //这个类的This变量
    private This thisRef = null;

    private Super superRef = null;

    private DefaultConstructor defaultConstructor = null;

    public Clazz(String name, ParserRuleContext ctx) {
        this.name = name;
        this.ctx = ctx;

        thisRef = new This(this, ctx);
        thisRef.type = this;

    }

    public Clazz getParentClass() {
        return parentClazz;
    }

    public void setParentClass(Clazz theClazz) {
        parentClazz = theClazz;

        //其实superRef引用的也是自己
        superRef = new Super(parentClazz, ctx);
        superRef.type = parentClazz;
    }

    //最顶层的基类
    private static Clazz rootClazz = new Clazz("Object", null);

    public This getThis() {
        return thisRef;
    }

    public Super getSuper() {
        return superRef;
    }

    @Override
    public String toString() {
        return "Class " + name;
    }

    /**
     * 是否包含某个Variable，包括自身及父类。
     *
     * @param name
     * @return
     */
    @Override
    public Variable getVariable(String name) {
        Variable rtn = super.getVariable(name);

        if (rtn == null && parentClazz != null) {
            rtn = parentClazz.getVariable(name);  //TODO 是否要检查visibility
        }

        return rtn;
    }


    /**
     * 是否包含某个Class
     *
     * @param name
     * @return
     */
    @Override
    public Clazz getClass(String name) {
        Clazz rtn = getClass(name);

        if (rtn == null && parentClazz != null) {
            rtn = parentClazz.getClass(name);  //TODO 是否要检查visibility?
        }

        return rtn;
    }

    /**
     * 找到某个构建函数。不需要往父类去找，在本级找就行了。
     *
     * @param paramTypes
     * @return
     */
    public Function findConstructor(List<Type> paramTypes) {
        Function rtn = super.getFunction(name, paramTypes);  //TODO 是否要检查visibility?

        return rtn;
    }

    /**
     * 在自身及父类中找到某个方法
     *
     * @param name
     * @param paramTypes 参数类型列表。该参数不允许为空。如果没有参数，需要传入一个0长度的列表。
     * @return
     */
    public Function getFunction(String name, List<Type> paramTypes) {
        //在本级查找这个这个方法
        Function rtn = super.getFunction(name, paramTypes);  //TODO 是否要检查visibility?

        //如果在本级找不到，那么递归的从父类中查找
        if (rtn == null && parentClazz != null) {
            rtn = parentClazz.getFunction(name, paramTypes);
        }

        return rtn;
    }

    public Variable getFunctionVariable(String name, List<Type> paramTypes) {
        Variable rtn = super.getFunctionVariable(name, paramTypes);  //TODO 是否要检查visibility?

        if (rtn == null && parentClazz != null) {
            rtn = parentClazz.getFunctionVariable(name, paramTypes);
        }

        return rtn;
    }

    /**
     * 是否包含某个Symbol。这时候要把父类的成员考虑进来。
     *
     * @param symbol
     * @return
     */
    @Override
    public boolean containsSymbol(Symbol symbol) {
        //this关键字
        if (symbol == thisRef || symbol == superRef) {
            return true;
        }

        boolean rtn = false;
        rtn = symbols.contains(symbol);
        if (!rtn && parentClazz != null) {
            rtn = parentClazz.containsSymbol(symbol);
        }
        return rtn;
    }

    /**
     * 当自身是目标类型的子类型的时候，返回true;
     *
     * @param type 目标类型
     * @return
     */
    @Override
    public boolean isType(Type type) {
        if (this == type) return true; //shortcut

        if (type instanceof Clazz) {
            return ((Clazz) type).isAncestor(this);
        }
        return false;
    }

    /**
     * 本类型是不是另一个类型的祖先类型
     *
     * @param theClazz
     * @return
     */
    public boolean isAncestor(Clazz theClazz) {
        if (theClazz.getParentClass() != null) {
            if (theClazz.getParentClass() == this) {
                return true;
            } else {
                return isAncestor(theClazz.getParentClass());
            }
        }
        return false;
    }

    public DefaultConstructor defaultConstructor() {
        if (defaultConstructor == null) {
            defaultConstructor = new DefaultConstructor(this.name, this);
        }
        return defaultConstructor;
    }

}